﻿#region Using Directives

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;

using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebControls;

using SharePointStu.SharePoint.General.Helpers;
using SharePointStu.SharePoint.General.Logging;
using SharePointStu.SharePoint.General.Cache;

#endregion Using Directives

namespace SharePointStu.ItemAuditing.Controls
{
    public partial class AuditLogView : UserControl
    {
        #region Public Members

        /// <summary>
        /// Type of audit reports.
        /// </summary>
        public enum AuditTypes
        {
            /// <summary>
            /// Audit report for <see cref="T:Microsoft.SharePoint.SPWeb"/> objects.
            /// </summary>
            Web,
            /// <summary>
            /// Audit report for <see cref="T:Microsoft.SharePoint.SPList"/> objects.
            /// </summary>
            List,
            /// <summary>
            /// Audit report for <see cref="T:Microsoft.SharePoint.SPListItem"/> objects.
            /// </summary>
            Item
        }

        #endregion Public Members

        #region Private Members

        ITemplate _templateHeader;
        AuditTypes _auditType;
        
        Audit.AbstractAuditEntries _auditEntriesObject;

        #endregion Private Members

        #region Constants

        const string NO_AUDIT_ITEMS_DEFAULT = "There are currently no audit items available to show.";
        // ViewState filter expression item.
        const string VS_FILTER = "FilterExpression";
        // Audit entries cache key.
        const string CACHE_AUDIT_ENTRIES = "AuditTable_{0}_{1}_{2}";
        // Cache override query string key.
        const string QS_ALLOW_CACHE = "nocache";

        #endregion Constants

        #region Public Properties

        [TemplateContainer(typeof(UserControl)), PersistenceMode(PersistenceMode.InnerProperty), TemplateInstance(TemplateInstance.Single)]
        public ITemplate Template_Header
        {
            get { return _templateHeader; }
            set { _templateHeader = value; }
        }

        /// <summary>
        /// Get or set the identifier for the <see cref="T:Microsoft.SharePoint.SPWeb"/>  object to retrieve the audit information for.
        /// </summary>
        public Guid WebId
        {
            get;
            set;
        }

        /// <summary>
        /// Get or set the identifier for the <see cref="T:Microsoft.SharePoint.SPList"/>  object to retrieve the audit information for.
        /// </summary>
        public Guid ListId
        {
            get;
            set;
        }

        /// <summary>
        /// Get or set the identifier for the <see cref="T:Microsoft.SharePoint.SPListItem"/>  object to retrieve the audit information for.
        /// </summary>
        public int ItemId
        {
            get;
            set;
        }

        /// <summary>
        /// Get or set the type of audit report.
        /// </summary>
        public AuditTypes AuditType
        {
            get { return _auditType; }
            set { _auditType = value; }
        }

        /// <summary>
        /// Get or set the message to display when there are no audit items to show.
        /// </summary>
        public string NoAudititemsMessage
        {
            get;
            set;
        }

        #endregion Public Properties

        #region Constructor

        public AuditLogView()
        {
            this.AuditType = AuditTypes.Web;
        }

        #endregion Constructor

        #region Overridden Methods

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);

            try
            {
                if (this.Template_Header != null)
                {
                    this.PlaceHolderAuditHeader.Controls.Clear();
                    this.Template_Header.InstantiateIn(this.PlaceHolderAuditHeader);
                }
            }
            catch (Exception ex)
            {
                UILogging.WriteError(PlaceHolderError, string.Format("Error in OnInit for SharePointStu.ItemAuditing.Controls.AuditLogView - Error was {0}.", ex.Message),
                                     "Error initialising audit log control.", EventId.Error, CategoryId.Auditing, ErrorLevel.High);
            }
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            try
            {
                ValidateControl();

                hiddenWebId.Value = WebId.ToString();
                hiddenListId.Value = ListId == Guid.Empty ? string.Empty : ListId.ToString();
                hiddenItemId.Value = ItemId.ToString();
                hiddenAuditType.Value = AuditType.ToString();

                string qs_cache = this.Page.Request.QueryString[QS_ALLOW_CACHE];
                bool allowCache = !(!string.IsNullOrEmpty(qs_cache) && qs_cache == "1");
                hiddenAllowCache.Value = allowCache.ToString();

                if (!Page.IsPostBack)
                {
                    AuditGrid.EmptyDataText = string.IsNullOrEmpty(NoAudititemsMessage) ? NO_AUDIT_ITEMS_DEFAULT : NoAudititemsMessage;

                    Audit.AbstractAuditEntries auditEntriesObject = null;
                    switch (AuditType)
                    {
                        case AuditTypes.Web:
                            auditEntriesObject = new Audit.WebAuditEntries(this.WebId);
                            break;
                        case AuditTypes.List:
                            auditEntriesObject = new Audit.ListAuditEntries(this.WebId, this.ListId);
                            break;
                        default:
                            auditEntriesObject = new Audit.ItemAuditEntries(this.WebId, this.ListId);
                            break;
                    }

                    // Define and add columns in DataTable to SPGridView.
                    foreach (Audit.AuditColumn column in auditEntriesObject.DefineColumns())
                    {
                        SPBoundField boundField = new SPBoundField();
                        boundField.HeaderText = column.Name;
                        boundField.DataField = column.Name;
                        if (AuditGrid.AllowSorting)
                        {
                            boundField.SortExpression = column.Name;
                        }
                        AuditGrid.Columns.Add(boundField);

                    }
                    // Setup filtering.
                    if (AuditGrid.AllowFiltering)
                    {
                        AuditGrid.FilterDataFields = auditEntriesObject.GetFilterColumns();
                        AuditGrid.FilteredDataSourcePropertyName = VS_FILTER;
                        AuditGrid.FilteredDataSourcePropertyFormat = "{1} = '{0}'";
                    }
                    AuditGrid.Sort(auditEntriesObject.DefaultSortColumn(), SortDirection.Descending);
                }

                AttachEvents();
            }
            catch (Exception ex)
            {
                UILogging.WriteError(PlaceHolderError, string.Format("Error in OnLoad for SharePointStu.ItemAuditing.Controls.AuditLogView - Error was {0}.", ex.Message),
                                     "Error loading audit log.", EventId.Error, CategoryId.Auditing, ErrorLevel.High);
            }
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);

            if (PlaceHolderError == null || PlaceHolderError.Controls.Count == 0)
            {
                PlaceHolderError.Visible = false;
                if (AuditGrid == null || AuditGrid.Rows.Count == 0)
                {
                    PlaceHolderAuditHeader.Visible = false;
                }
                else
                {
                    PlaceHolderAuditHeader.Visible = true;
                }
            }
            else
            {
                PlaceHolderError.Visible = true;
                PlaceHolderAuditHeader.Visible = false;
            }
        }

        protected override void LoadViewState(object savedState)
        {
            base.LoadViewState(savedState);

            if (Context.Request.Form["__EVENTARGUMENT"] != null && Context.Request.Form["__EVENTARGUMENT"].EndsWith("__ClearFilter__"))
            {
                ViewState.Remove(VS_FILTER);
            }
        }

        #endregion Overridden Methods

        #region Events

        /// <summary>
        /// Fires when the user clicks the Refresh button
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void RefreshLogButton_OnClick(object sender, EventArgs e)
        {
            string cacheKey = string.Format(CACHE_AUDIT_ENTRIES, this.WebId, this.ListId == Guid.Empty ? string.Empty : this.ListId.ToString(), this.ItemId <= 0 ? string.Empty : this.ItemId.ToString());

            CacheManager.Instance.Delete(cacheKey);
            AuditGrid.DataBind();
        }

        /// <summary>
        /// Fires when the user clicks the Cancel button
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void CancelButton_OnClick(object sender, EventArgs e)
        {
            string url = this.Context.Request.QueryString["CancelSource"];
            if (SPUtility.RedirectValidate(url, this.Context))
            {
                Response.Redirect(url);
            }
            else
            {
                SPUtility.Redirect("settings.aspx", SPRedirectFlags.RelativeToLayoutsPage, this.Context);
            }
        }

        /// <summary>
        /// Fires when the user changes the page displayed in the SharePoint grid.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void AuditGrid_PageIndexChanging(object sender, GridViewPageEventArgs e)
        {
            ObjectDataSourceAuditItems.FilterExpression = (string)ViewState[VS_FILTER];
            AuditGrid.PageIndex = e.NewPageIndex;
            AuditGrid.DataBind();
        }

        /// <summary>
        /// Fires when the user sorts a column in the grid.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void AuditGrid_Sorting(object sender, GridViewSortEventArgs e)
        {
            if (ViewState[VS_FILTER] != null)
            {
                ObjectDataSourceAuditItems.FilterExpression = (string)ViewState[VS_FILTER];
            }
        }

        /// <summary>
        /// Fires when the user filters a column in the grid.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void ObjectDataSourceAuditItems_Filtering(object sender, ObjectDataSourceFilteringEventArgs e)
        {
            ViewState[VS_FILTER] = ((ObjectDataSourceView)sender).FilterExpression;
        }

        /// <summary>
        /// Fires when a row in the grid is bound.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void AuditGrid_RowDataBound(object sender, GridViewRowEventArgs e)
        {
            if (sender == null || e.Row.RowType != DataControlRowType.Header || string.IsNullOrEmpty(AuditGrid.FilterFieldName))
            {
                return;
            }

            // Show icon on the filtered column.
            for (int i = 0; i < AuditGrid.Columns.Count; i++)
            {
                DataControlField field = AuditGrid.Columns[i];

                if (field.SortExpression == AuditGrid.FilterFieldName)
                {
                    Image filterIcon = new Image();
                    filterIcon.ImageUrl = "/_layouts/images/filter.gif";
                    filterIcon.Style[HtmlTextWriterStyle.MarginLeft] = "2px";

                    Literal headerText = new Literal();
                    headerText.Text = field.HeaderText;

                    PlaceHolder panel = new PlaceHolder();
                    panel.Controls.Add(headerText);
                    panel.Controls.Add(filterIcon);

                    e.Row.Cells[i].Controls[0].Controls.Add(panel);
                    break;
                }
            }
        }

        #endregion Events

        #region Public Methods

        /// <summary>
        /// Get audit items for the requested type.
        /// </summary>
        /// <param name="webId">The identifier for the parent SPWeb object.</param>
        /// <param name="listId">Identifier for the list.</param>
        /// <param name="itemId">Identifier for the item.</param>
        /// <param name="auditType">The type of audit to retrieve information for.</param>
        /// <param name="allowCache">If caching should be used.</param>
        /// <returns>A <see cref="T:System.Data.DataTable"/> object.</returns>
        [DataObjectMethod(DataObjectMethodType.Select)]
        public DataTable GetAuditItems(string webId, string listId, int itemId, string auditType, bool allowCache)
        {
            string cacheKey = string.Format(CACHE_AUDIT_ENTRIES, webId, (string.IsNullOrEmpty(listId) ? string.Empty : listId), itemId <= 0 ? string.Empty : itemId.ToString());

            if (allowCache)
            {
                _auditEntriesObject = CacheManager.Instance.GetValue(cacheKey) as Audit.AbstractAuditEntries;
            }

            if (_auditEntriesObject != null && _auditEntriesObject.Table != null)
            {
                return _auditEntriesObject.Table;
            }

            SPAuditEntryCollection auditEntryCollection = null;
            if (Common.IsGuid(webId) && !string.IsNullOrEmpty(auditType))
            {
                try
                {
                    Guid webGuid = new Guid(webId);
                    AuditTypes aType = (AuditTypes)Enum.Parse(typeof(AuditTypes), auditType);

                    Guid siteId = SPContext.Current.Site.ID;

                    SPSecurity.RunWithElevatedPrivileges(delegate
                    {
                        using (SPSite site = new SPSite(siteId))
                        {
                            using (SPWeb web = site.OpenWeb(webGuid))
                            {
                                SPList list = null;
                                SPAuditQuery query = new SPAuditQuery(web.Site);
                                switch (aType)
                                {
                                    case AuditTypes.Web:
                                        auditEntryCollection = web.Audit.GetEntries(query);
                                        break;
                                    case AuditTypes.List:
                                        list = web.Lists[new Guid(listId)];
                                        query.RestrictToList(list);
                                        auditEntryCollection = web.Audit.GetEntries(query);
                                        break;
                                    case AuditTypes.Item:
                                        list = web.Lists[new Guid(listId)];
                                        SPListItem item = list.GetItemById(itemId);
                                        query.RestrictToListItem(item);
                                        auditEntryCollection = web.Audit.GetEntries(query);
                                        break;
                                }
                            }
                        }
                    });

                    if (auditEntryCollection != null)
                    {
                        DataTable auditTable = null;
                        _auditEntriesObject = null;
                        switch (aType)
                        {
                            case AuditTypes.Web:
                                _auditEntriesObject = new Audit.WebAuditEntries(webGuid);
                                break;
                            case AuditTypes.List:
                                _auditEntriesObject = new Audit.ListAuditEntries(webGuid, new Guid(listId));
                                break;
                            case AuditTypes.Item:
                                _auditEntriesObject = new Audit.ItemAuditEntries(webGuid, new Guid(listId));
                                break;
                        }

                        _auditEntriesObject.AddRows(auditEntryCollection);
                        auditTable = _auditEntriesObject.Table;
                        if (allowCache)
                        {
                            CacheManager.Instance.Add(cacheKey, _auditEntriesObject, DateTime.Now.AddMinutes(10), System.Web.Caching.CacheItemPriority.Normal);
                        }
                        return auditTable;
                    }
                }
                catch (Exception ex)
                {
                    string inner = "Nothing";
                    if (ex.InnerException != null && !string.IsNullOrEmpty(ex.InnerException.Message))
                    {
                        inner = ex.InnerException.Message;
                    }
                    Logger.LogErrorLocal(EventId.Error, CategoryId.Auditing, ErrorLevel.High,
                                         string.Format("Error in GetAuditItems for SharePointStu.ItemAuditing.Controls.AuditLogView - Audit type was {0} and the error was: {1}.  Inner exception was: {2}.",
                                         AuditType, ex.Message, inner),
                                         ex);
                }
            }
            return null;
        }

        #endregion Public Methods

        #region Private Methods

        /// <summary>
        /// Validate this control.
        /// </summary>
        /// <exception cref="T:SharePointStu.ItemAuditing.InvalidWebIdentifierException"></exception>
        /// <exception cref="T:SharePointStu.ItemAuditing.InvalidListIdentifierException"></exception>
        /// <exception cref="T:SharePointStu.ItemAuditing.InvalidListItemIdentifierException"></exception>
        void ValidateControl()
        {
            if (this.WebId == Guid.Empty)
            {
                throw new InvalidWebIdentifierException(this.AuditType);
            }
            if (this.AuditType == AuditTypes.List && this.ListId == Guid.Empty)
            {
                throw new InvalidListIdentifierException(this.WebId, this.AuditType);
            }
            if (this.AuditType == AuditTypes.Item && (this.ItemId <= 0 || this.ListId == Guid.Empty))
            {
                throw new InvalidListItemIdentifierException(this.WebId, this.ListId, this.ItemId, this.AuditType);
            }
        }

        /// <summary>
        /// Attach all required events.
        /// </summary>
        void AttachEvents()
        {
            if (AuditGrid.AllowPaging)
            {
                AuditGrid.PageIndexChanging += AuditGrid_PageIndexChanging;
                AuditGrid.PagerTemplate = null;
            }
            if (AuditGrid.AllowSorting)
            {
                AuditGrid.Sorting += AuditGrid_Sorting;
            }
            if (AuditGrid.AllowFiltering)
            {
                AuditGrid.RowDataBound += AuditGrid_RowDataBound;
                ObjectDataSourceAuditItems.Filtering += ObjectDataSourceAuditItems_Filtering;
            }
        }

        #endregion Private Methods
    }
}
